From e46b866a1702eab1390dabb984b9ee8aea440cba Mon Sep 17 00:00:00 2001 From: robertl Date: Sat, 12 Mar 2005 05:27:30 +0000 Subject: [PATCH] Add lowrance usr support from Jason Rust/jrust at rustyparts.com. --- gpsbabel/Makefile | 3 +- gpsbabel/README | 8 + gpsbabel/lowranceusr.c | 372 ++++++++++++++++++++++++++++++++ gpsbabel/reference/lowrance.usr | Bin 0 -> 353 bytes gpsbabel/testo | 11 + gpsbabel/vecs.c | 7 + 6 files changed, 400 insertions(+), 1 deletion(-) create mode 100644 gpsbabel/lowranceusr.c create mode 100644 gpsbabel/reference/lowrance.usr diff --git a/gpsbabel/Makefile b/gpsbabel/Makefile index 7dd93ec34..cb2137150 100644 --- a/gpsbabel/Makefile +++ b/gpsbabel/Makefile @@ -27,7 +27,7 @@ FMTS=magproto.o gpx.o geo.o mapsend.o mapsource.o garmin_tables.o \ gpilots.o saroute.o navicache.o psitrex.o geoniche.o delgpl.o \ ozi.o nmea.o text.o html.o palmdoc.o netstumbler.o hsa_ndv.o \ igc.o brauniger_iq.o shape.o hiketech.o glogbook.o coastexp.o \ - vcf.o overlay.o google.o + vcf.o overlay.o google.o lowranceusr.o FILTERS=position.o duplicate.o arcdist.o polygon.o smplrout.o reverse_route.o sort.o stackfilter.o @@ -170,6 +170,7 @@ html.o: html.c defs.h queue.h gbtypes.h jeeps/gpsmath.h jeeps/gps.h \ jeeps/gpsinput.h jeeps/gpsproj.h jeeps/gpsnmeafmt.h jeeps/gpsnmeaget.h igc.o: igc.c defs.h queue.h gbtypes.h internal_styles.o: internal_styles.c defs.h queue.h gbtypes.h +lowranceusr.o: lowranceusr.c defs.h queue.h gbtypes.h magnav.o: magnav.c defs.h queue.h gbtypes.h coldsync/palm.h \ coldsync/pdb.h magproto.o: magproto.c defs.h queue.h gbtypes.h magellan.h diff --git a/gpsbabel/README b/gpsbabel/README index 30c425e83..831a5ac6b 100644 --- a/gpsbabel/README +++ b/gpsbabel/README @@ -134,6 +134,14 @@ THE FORMATS manager) for iFinder which is available at http://www.lowrance.com/Software/GDM6/Default.asp + Lowrance USR + + The Lowrance iFinder GPS series has the unique capability to output + its data to an MMC card. The data is saved to the card as a .USR + file and can be read by your computer using a card reader. + Currently only reading and writing of waypoints (no routes or + tracks) is supported. + XMap Delorme TopoUSA/XMap Conduit is one of the billion CSV variants diff --git a/gpsbabel/lowranceusr.c b/gpsbabel/lowranceusr.c new file mode 100644 index 000000000..9a1a233f8 --- /dev/null +++ b/gpsbabel/lowranceusr.c @@ -0,0 +1,372 @@ +/* + Access to Lowrance USR files. + Contributed to gpsbabel by Jason Rust (jrust at rustyparts.com) + + Copyright (C) 2005 Robert Lipe, robertlipe@usa.net + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111 USA +*/ + + +#include "defs.h" +#include +#include /* for lat/lon conversion */ + +typedef struct lowranceusr_icon_mapping { + const long int value; + const char *icon; +} lowranceusr_icon_mapping_t; + +/* Taken from iFinder 1.8 */ +const lowranceusr_icon_mapping_t lowranceusr_icon_value_table[] = { + { 10000, "diamond 1" }, + { 10001, "diamond 2" }, + { 10002, "diamond 3" }, + { 10003, "x 1" }, + { 10004, "x 2" }, + { 10005, "x 3" }, + { 10006, "cross" }, + { 10007, "house" }, + { 10008, "car" }, + { 10009, "store" }, + { 10010, "gas station" }, + { 10011, "fork and spoon" }, + { 10012, "telephone" }, + { 10013, "airplane" }, + { 10014, "exit sign" }, + { 10015, "stop sign" }, + { 10016, "exclamation" }, + { 10017, "traffic light" }, + { 10018, "american flag" }, + { 10019, "person" }, + { 10020, "restrooms" }, + { 10021, "tree" }, + { 10022, "mountains" }, + { 10023, "campsite" }, + { 10024, "picnic table" }, + { 10025, "deer" }, + { 10026, "deer tracks" }, + { 10027, "turkey tracks" }, + { 10028, "tree stand" }, + { 10029, "bridge" }, + { 10030, "skull and crossbones" }, + { 10031, "fish" }, + { 10032, "two fish" }, + { 10033, "dive flag" }, + { 10034, "wreck" }, + { 10035, "anchor" }, + { 10036, "boat" }, + { 10037, "boat ramp" }, + { 10038, "flag buoy" }, + { 10039, "dam" }, + { 10040, "swimmer" }, + { 10041, "pier"}, + + { 10038, "Micro-Cache" }, /* icon for "flag buoy" */ + { 10030, "Virtual cache" }, /* icon for "skull and crossbones" */ + { 10032, "Multi-Cache" }, /* icon for "two fish" */ + { 10003, "Unknown Cache" }, /* icon for "x 1" */ + { 10018, "Locationless (Reverse) Cache" }, /* Icon for "american flag" */ + { 10007, "Post Office" }, /* Icon for "house" */ + { 10019, "Event Cache" }, /* Icon for "person" */ + { 10020, "Webcam Cache" }, /* Icon for "restrooms" */ + { -1, NULL } +}; + +static FILE *file_in; +static FILE *file_out; +static void *mkshort_handle; + +static unsigned int waypt_out_count; + +#define MYNAME "Lowrance USR" + +#define MAXUSRSTRINGSIZE 256 +#define SEMIMINOR 6356752.3142 +#define DEGREESTORADIANS 0.017453292 +#define SECSTO2000 946713599 + +const char * +lowranceusr_find_desc_from_icon_number(const int icon) +{ + const lowranceusr_icon_mapping_t *i; + + for (i = lowranceusr_icon_value_table; i->icon; i++) { + if (icon == i->value) { + return i->icon; + } + } + + return ""; +} + +long int +lowranceusr_find_icon_number_from_desc(const char *desc) +{ + const lowranceusr_icon_mapping_t *i; + long int def_icon = 10001; + + if (!desc) { + return def_icon; + } + + for (i = lowranceusr_icon_value_table; i->icon; i++) { + if (case_ignore_strcmp(desc,i->icon) == 0) { + return i->value; + } + } + + return def_icon; +} +static int +lowranceusr_fread(void *buff, size_t size, size_t members, FILE * fp) +{ + size_t br; + + br = fread(buff, size, members, fp); + + if (br != members) { + fatal(MYNAME ": requested to read %d bytes, read %d bytes.\n", members, br); + } + + return (br); +} + +static +arglist_t lowranceusr_args[] = { + {0, 0, 0, 0,0 } +}; + +static void +rd_init(const char *fname) +{ + file_in = xfopen(fname, "rb", MYNAME); +} + +static void +rd_deinit(void) +{ + fclose(file_in); +} + +static void +wr_init(const char *fname) +{ + file_out = xfopen(fname, "wb", MYNAME); + mkshort_handle = mkshort_new_handle(); + waypt_out_count = 0; +} + +static void +wr_deinit(void) +{ + fclose(file_out); + mkshort_del_handle(mkshort_handle); +} + +/** + * Latitude and longitude for USR coords are in the lowrance mercator meter + * format in WGS84. The below code converts them to degrees. + */ +static double +lon_mm_to_deg(double x) { + return x / (DEGREESTORADIANS * SEMIMINOR); +} + +static long +lon_deg_to_mm(double x) { + return (long)(x * SEMIMINOR * DEGREESTORADIANS); +} + +static double +lat_mm_to_deg(double x) { + return (2 * atan(exp(x / SEMIMINOR)) - M_PI / 2) / DEGREESTORADIANS; +} + +static long +lat_deg_to_mm(double x) { + return (long)(SEMIMINOR * log(tan((x * DEGREESTORADIANS + M_PI / 2) / 2))); +} + +static void +data_read(void) +{ + char buff[MAXUSRSTRINGSIZE + 1]; + short int NumWaypoints, MajorVersion, MinorVersion; + int i; + long int TextLen; + + lowranceusr_fread(&buff[0], 2, 1, file_in); + MajorVersion = le_read16(&buff[0]); + lowranceusr_fread(&buff[0], 2, 1, file_in); + MinorVersion = le_read16(&buff[0]); + + if (MajorVersion < 2) { + fatal(MYNAME ": input file is from an old version of the USR file and is not supported\n"); + } + + lowranceusr_fread(&buff[0], 2, 1, file_in); + NumWaypoints = le_read16(&buff[0]); + for (i = 0; i < NumWaypoints; i++) { + waypoint *wpt_tmp; + + wpt_tmp = waypt_new(); + + /* Object num */ + lowranceusr_fread(&buff[0], 2, 1, file_in); + lowranceusr_fread(&buff[0], 4, 1, file_in); + wpt_tmp->latitude = lat_mm_to_deg(le_read32(&buff[0])); + lowranceusr_fread(&buff[0], 4, 1, file_in); + wpt_tmp->longitude = lon_mm_to_deg(le_read32(&buff[0])); + lowranceusr_fread(&buff[0], 4, 1, file_in); + wpt_tmp->altitude = le_read32(&buff[0]); + + lowranceusr_fread(&buff[0], 4, 1, file_in); + TextLen = buff[0]; + lowranceusr_fread(&buff[0], TextLen, 1, file_in); + buff[TextLen] = '\0'; + wpt_tmp->shortname = xstrdup(buff); + + lowranceusr_fread(&buff[0], 4, 1, file_in); + TextLen = buff[0]; + if (TextLen) { + lowranceusr_fread(&buff[0], TextLen, 1, file_in); + buff[TextLen] = '\0'; + wpt_tmp->description = xstrdup(buff); + } + + lowranceusr_fread(&buff[0], 4, 1, file_in); + /* Time is number of seconds since Jan. 1, 2000 */ + wpt_tmp->creation_time = SECSTO2000 + le_read32(&buff[0]); + + /* Symbol ID */ + lowranceusr_fread(&buff[0], 4, 1, file_in); + wpt_tmp->icon_descr = lowranceusr_find_desc_from_icon_number(le_read32(&buff[0])); + + /* Waypoint Type (USER, TEMPORARY, POINT_OF_INTEREST) */ + lowranceusr_fread(&buff[0], 2, 1, file_in); + + waypt_add(wpt_tmp); + } +} + +static void +lowranceusr_waypt_pr(const waypoint *wpt) +{ + long int TextLen, Lat, Lon, Time, SymbolId; + short int WayptType; + char *name; + char *comment; + + /* our personal waypoint counter */ + fwrite(&waypt_out_count, 2, 1, file_out); + waypt_out_count++; + + Lat = lat_deg_to_mm(wpt->latitude); + fwrite(&Lat, 4, 1, file_out); + Lon = lon_deg_to_mm(wpt->longitude); + fwrite(&Lon, 4, 1, file_out); + fwrite(&wpt->altitude, 4, 1, file_out); + + /* Try and make sure we have a name */ + if ((! wpt->shortname) || global_opts.synthesize_shortnames) { + if (wpt->description && global_opts.synthesize_shortnames) { + name = mkshort(mkshort_handle, wpt->description); + } else if (wpt->shortname) { + name = xstrdup(wpt->shortname); + } else if (wpt->description) { + name = xstrdup(wpt->description); + } else { + name = xstrdup(""); + } + } else { + name = xstrdup(wpt->shortname); + } + + TextLen = strlen(name); + fwrite(&TextLen, 4, 1, file_out); + fwrite(name, 1, TextLen, file_out); + xfree(name); + + /** + * Comments aren't used by the iFinder yet so they just take up space... + */ + if (0 && wpt->description && strcmp(wpt->description, wpt->shortname) != 0) { + comment = xstrdup(wpt->description); + TextLen = strlen(comment); + fwrite(&TextLen, 4, 1, file_out); + fwrite(comment, 1, TextLen, file_out); + xfree(comment); + } else { + TextLen = 0; + fwrite(&TextLen, 4, 1, file_out); + } + + if (wpt->creation_time > SECSTO2000) { + Time = wpt->creation_time - SECSTO2000; + } else { + Time = SECSTO2000 + 1; + } + fwrite(&Time, 4, 1, file_out); + + if (get_cache_icon(wpt) && wpt->icon_descr && (strcmp(wpt->icon_descr, "Geocache Found") != 0)) { + SymbolId = lowranceusr_find_icon_number_from_desc(get_cache_icon(wpt)); + } else { + SymbolId = lowranceusr_find_icon_number_from_desc(wpt->icon_descr); + } + + fwrite(&SymbolId, 4, 1, file_out); + + /* USER waypoint type */ + WayptType = 0; + fwrite(&WayptType, 2, 1, file_out); +} + +static void +data_write(void) +{ + short int NumWaypoints, MajorVersion, MinorVersion, NumRoutes, NumTrails, NumIcons; + setshort_length(mkshort_handle, 15); + MajorVersion = 2; + MinorVersion = 0; + NumWaypoints = waypt_count(); + + fwrite(&MajorVersion, 2, 1, file_out); + fwrite(&MinorVersion, 2, 1, file_out); + fwrite(&NumWaypoints, 2, 1, file_out); + waypt_disp_all(lowranceusr_waypt_pr); + + /* We don't support these yet... */ + NumRoutes = 0; + fwrite(&NumRoutes, 2, 1, file_out); + NumIcons = 0; + fwrite(&NumIcons, 2, 1, file_out); + NumTrails = 0; + fwrite(&NumTrails, 2, 1, file_out); +} + + +ff_vecs_t lowranceusr_vecs = { + ff_type_file, + FF_CAP_RW_WPT, + rd_init, + wr_init, + rd_deinit, + wr_deinit, + data_read, + data_write, + NULL, + lowranceusr_args +}; diff --git a/gpsbabel/reference/lowrance.usr b/gpsbabel/reference/lowrance.usr new file mode 100644 index 0000000000000000000000000000000000000000..1982f8a1a27e915833378db2710bd82c8eac9f02 GIT binary patch literal 353 zcmZQ#U|`?`;{V}}3^uhn|A9;fRv>nFc6D+BaTvDbS_rCx7>o>4r#mv#I^}{Du>nQg zoedq0%|UVqRZI-OQXCoVV^CB%8=;xS%rK+ck>RO7vMM7}M@v+*SQum%J2JGXqo^`B zGDcO!$`IJ;$dD$4qRPqK5LFc$1Isi=1|cqFRmKLEZm6o*8BVo2GGsr41O&v7#)jsO XsH!*^q9!{sD1Jm!<$|gT%wqrmf`>Y7 literal 0 HcmV?d00001 diff --git a/gpsbabel/testo b/gpsbabel/testo index fa01bc0f1..d8646aecb 100755 --- a/gpsbabel/testo +++ b/gpsbabel/testo @@ -80,6 +80,17 @@ ${PNAME} -i geo -f geocaching.loc -o tiger -F ${TMPDIR}/tiger ${PNAME} -i tiger -f ${TMPDIR}/tiger -o tiger -F ${TMPDIR}/tiger2 compare ${TMPDIR}/tiger ${TMPDIR}/tiger2 +# +# Lowrance USR. Binary, and also slightly lossy because of the math to +# convert lat/long. +# +rm -f ${TMPDIR}/lowrance1.usr +${PNAME} -i geo -f geocaching.loc -o lowranceusr -F ${TMPDIR}/lowrance1.usr +bincompare ${TMPDIR}/lowrance1.usr reference/lowrance.usr +${PNAME} -i lowranceusr -f ${TMPDIR}/lowrance1.usr -o gpx -o ${TMPDIR}/lowrance1.out +${PNAME} -i geo -f geocaching.loc -o gpx -F ${TMPDIR}/lowrance2.out +compare ${TMPDIR}/lowrance1.out ${TMPDIR}/lowrance2.out + # CSV (Comma separated value) data. ${PNAME} -i geo -f geocaching.loc -o csv -F ${TMPDIR}/csv.csv diff --git a/gpsbabel/vecs.c b/gpsbabel/vecs.c index 83dbab313..bbc0e6430 100644 --- a/gpsbabel/vecs.c +++ b/gpsbabel/vecs.c @@ -39,6 +39,7 @@ extern ff_vecs_t mps_vecs; extern ff_vecs_t gpsutil_vecs; extern ff_vecs_t tiger_vecs; extern ff_vecs_t pcx_vecs; +extern ff_vecs_t lowranceusr_vecs; extern ff_vecs_t cetus_vecs; extern ff_vecs_t gpspilot_vecs; extern ff_vecs_t copilot_vecs; @@ -138,6 +139,12 @@ vecs_t vec_list[] = { "MS PocketStreets 2002 Pushpin", "psp" }, + { + &lowranceusr_vecs, + "lowranceusr", + "Lowrance USR", + NULL + }, { &cetus_vecs, "cetus", -- 2.30.2